Tornado 구현

에이펙스 레전드의 호라이즌의 스킬 Q(중력리프트) 와 비슷하게 구현하고 싶었다.

수정

1.2

1.0버전과 같이 AddForce를 통해 구현했다. 1.1의 경우 Tornado가 겹쳐있을때 버그가 발생하기 쉽고, 고치기 는 번거로워 AddForce를 통해 구현한다.

void ATornado::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	TArray<AActor*> FlyActorsToIgnore;
	while (true)
	{
		FHitResult HitResult;
		BoxTrace(HitResult, FlyActorsToIgnore);
		if (HitResult.GetActor() == nullptr) break;
		if (HitResult.GetActor() == GetOwner())
		{
			ACharacter* Character = Cast<ACharacter>(HitResult.GetActor());
			if (Character)
			{
				Character->GetCharacterMovement()->Velocity.Z = UpSpeed;
			}
		}
	}
}

void ATornado::BeginBoxOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	ACharacter* Character = Cast<ACharacter>(OtherActor);
	if (Character)
	{
		Character->GetCharacterMovement()->AddForce(10000000.0f * FVector::ZAxisVector);
	}
}

1.1

SetMovementMode

아래 방법과 달리 CharacterMovementComponent의 SetMovementMode를 통해 Fly모드로 만들어 구현하는 방식으로 변경. Tornado에 오버랩 될때 Fly로 바꾸고, 오버랩에서 빠져나올때 Walking으로 되돌려 준다.

void ATornado::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	TArray<AActor*> FlyActorsToIgnore;
	while (true)
	{
		FHitResult HitResult;
		BoxTrace(HitResult, FlyActorsToIgnore);
		if (HitResult.GetActor() == nullptr) break;
		if (HitResult.GetActor() == GetOwner())
		{
			ACharacter* Character = Cast<ACharacter>(HitResult.GetActor());
			if (Character)
			{
				UCharacterMovementComponent* MovementComponent = Character->GetCharacterMovement();
				if (MovementComponent)
				{
					if (!IsTopLocation(Character))
					{
						MovementComponent->AddInputVectorZAxisVector;
					}
					else
					{
						MovementComponent->Velocity.Z = 0.0f;
					}
				}
			}
		}
	}
}

void ATornado::BeginBoxOverlapExec(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	ACharacter* Character = Cast<ACharacter>(OtherActor);
	if (Character)
	{
		UCharacterMovementComponent* MovementComponent = Character->GetCharacterMovement();
		if (MovementComponent)
		{
			MovementComponent->SetMovementModeMOVE_Flying;
		}
	}
}

void ATornado::EndBoxOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	ACharacter* Character = Cast<ACharacter>(OtherActor);
	if (Character)
	{
		UCharacterMovementComponent* MovementComponent = Character->GetCharacterMovement();
		if (MovementComponent)
		{
			MovementComponent->SetMovementModeMOVE_Walking;
			if (IsTopLocation(Character))
			{
				MovementComponent->AddForce(UpForce * 10000000.0f * FVector::ZAxisVector);
			}
		}
	}
}

1.0

AddForce 함수

AddForce만 사용해 구현하여 위로 상승하게 하는 경우, 말 그대로 힘을 가하는 경우이기 때문에 속력을 일정하게 유지할 수 없다. 즉 계속해서 가속해 속도가 올라가다 가장 위에서 멈추지 못하고 튕겨져 나가게 된다.

CharacterMovementComponent의 Velocity

Velocity는 FVector형식의 멤버변수인데 Get, Set을 통하여 접근하지않고 바로 접근(public)이 가능하다. 다만 이 변수를 직접적으로 변경하게 될 경우, Z축 의 경우 중력 영향인지 바로 움직이지 않는다. 즉 이미 Z축방향으로 운동중인 경우에만 설정한 Velocity에 맞게 속력이 나오게 된다. 따라서 AddForce와 조합해서 사용하기로 했다.

void ATornado::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	InitActorsToIgnore();
	while (true)
	{
		FHitResult HitResult;
		BoxTrace(HitResult);
		if (HitResult.GetActor() == nullptr) break;
		if (HitResult.GetActor() == GetOwner())
		{
			ACharacter* Character = Cast<ACharacter>(HitResult.GetActor());
			if (Character)
			{
				if (!IsTopLocation(Character))
				{
					if (Character->GetCharacterMovement()->Velocity.Z < 1)
						Character->GetCharacterMovementZAxisVector * 100000.0f; // 대략적으로 캐릭터가 Z방향으로 움직이게 하는 최소 힘이 100000정도 됨
					Character->GetCharacterMovement()->Velocity.Z = UpSpeed;
				}
				else
					Character->GetCharacterMovement()->Velocity.Z = TopSpeed;
			}
		}
	}
}